/*!\file  std_io.c  The math code.
 *
 *      This file is part of the micro-rtos package.
 *
 *  Copyright (c) 2008-2009 by 
		Jan Vysohlid <janvysohlid@gmail.com>
		Gabriel Zabusek <gabriel.zabusek@gmail.com>
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 */


#include <include/math.h>
#include <drivers/lcd/ZT018/ZT018.h>
#include <include/std_io.h>

int _putc( int c )
{
	putch( c );
	return 1;
}

size_t puts (const char *str)
{
	size_t char_counter = 0;

	while (*str) {
		char_counter += _putc (*str++);
	}

	return char_counter;
}

/** Print a hexadecimal number
 *
 * @param number The number to print.
 *
 * @return Number of characters printed.
 *
 */
static size_t print_hex (const uint32_t number)
{
	size_t char_counter = 0;
	char *hex_digits = "0123456789abcdef";
	int i;

	/* extract bytes from number and write it one by one */
	for (i = sizeof (number) * 8 - 4; i >= 0; i -= 4) {
		char_counter += _putc (*(hex_digits + ((number >> i) & 0xf)));
	}

	return char_counter;
}

/** Print an unsigned decimal number
 *
 * @param number The number to print.
 *
 * @return Number of characters printed.
 *
 */
static size_t print_unsigned (const uint32_t number)
{
	int value = number;
	int decimal_base = 10;
	char *hex_digits = "0123456789abcdef";
	char buffer[sizeof (uint32_t) * 4 + 1];
	int i = sizeof (uint32_t) * 4 - 1;

	/* buffer is a zero terminated string */
	buffer[sizeof (uint32_t) * 4] = 0;

	/* insert digits of the number to buffer */
	do {
		buffer[i--] = hex_digits[value % decimal_base];
	} while (value /= decimal_base);

//	/* print digits from the buffer */
	return puts (&buffer[i + 1]);

}

/** Print a signed decimal number
 *
 * @param number The number to print.
 *
 * @return Number of characters printed.
 *
 */
static size_t print_int (const int32_t number)
{
	size_t char_counter = 0;
	int value;

	/* change to unsigned and print minus if needed */
	if (number < 0) {
		char_counter += _putc ('-');
		value = -number;	
	}
	else {
		value = number;	
	}

	/* print unsigned decimal number */
	char_counter += print_unsigned ((uint32_t) value);

	return char_counter;
}

/** Print a zero terminated formated string. Formatting support for
 *  char %c, string %s, decimal %d, %u, %i, hexadeciaml %x, pointer %p
 *
 * @param format The formated string to print.
 *
 * @return Number of characters printed.
 *
 */
size_t printk (const char *format, ...)
{
	size_t char_counter = 0;
	int i = 0;
	va_list va_list_pointer;
	char c;
	
	/* va_list inicialization */
	va_start (va_list_pointer, format);

	while ((c = format[i++]))	{
		switch (c) {
			/* format control character */
			case '%':
				switch (c = format[i++]) {
					/* print '%' */
					case '%':
						char_counter += _putc (c);
						break;
					/* print character */
					case 'c':
						char_counter += _putc ((char) va_arg (va_list_pointer, int));
						break;
					/*  print string */
					case 's':
						char_counter += puts (va_arg (va_list_pointer, char *));
						break;
					/*  print signed decimal number */
					case 'd':
					case 'i':
						char_counter += print_int (va_arg (va_list_pointer, int32_t));
						break;
					/*  print unsigned decimal number */
					case 'u':
						char_counter += print_unsigned (va_arg (va_list_pointer, uint32_t));
						break;
					/*  print hexadecimal number */
					case 'x':
					/*  print pointer address */
					case 'p':
						char_counter += print_hex (va_arg (va_list_pointer, uint32_t));
						break;
					/*  print '%' and the actual character */
					default:
						char_counter += _putc ('%');
						char_counter += _putc (c);
						break;
				}
				break;
			/* other characters print */
			default:
				char_counter += _putc(c);
				break;
		}
	}

	/* va_list destruction */
	va_end (va_list_pointer);

	return char_counter;
}
